﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Environment.Configuration;
using Orchard.FileSystems.Media;
using Orchard.Localization;
using EDB.SmoothStreaming.Models;
using EDB.SmoothStreaming.Services;
using EDB.SmoothStreaming.ViewModels;

namespace EDB.SmoothStreaming.Drivers {
    public class VideoDriver : ContentPartDriver<VideoPart> {
        private readonly IOrchardServices _orchardServices;
        private readonly IStorageProvider _storageProvider;
        private readonly IVideoService _videoService;
        private readonly ShellSettings _settings;
        private readonly List<string> _allowedFileFormats;
        private readonly List<string> _allowedCompressedFileFormats;

        private readonly string _videoRootFolder;

        public Localizer T { get; set; }

        public VideoDriver(IOrchardServices services, IStorageProvider storageProvider, IVideoService videoService, ShellSettings settings) {
            T = NullLocalizer.Instance;
            _orchardServices = services;
            _storageProvider = storageProvider;
            _videoService = videoService;
            _settings = settings;
            _videoRootFolder = "SmoothStreaming";
            _allowedFileFormats = new List<string>(new string[] { ".3g2", ".3gp", ".asf", ".avi", ".avs", ".dv", ".dvr-ms", ".ismv", ".m2v", ".m4v", ".mod", ".mov", ".mov", ".mpeg", ".mpg", ".mts", ".m2ts", ".ts", ".vob", ".wmv", ".wtv", ".xesc" });
            _allowedCompressedFileFormats = new List<string>(new string[] { ".zip" });
        }

        protected override DriverResult Display(VideoPart part, string displayType, dynamic shapeHelper) {
            VideoViewModel viewModel = new VideoViewModel();
            viewModel.VideoId = part.VideoId;
            viewModel.ThumbnailCount = part.ThumbnailCount;
            viewModel.PlayerWidth = part.PlayerWidth;
            viewModel.PlayerHeight = part.PlayerHeight;
            viewModel.Duration = part.Duration;
            viewModel.ThumbnailFormat = part.ThumbnailFormat;

            Uri url = _orchardServices.WorkContext.HttpContext.Request.Url;
            string domainName = String.Format("{0}://{1}{2}", url.Scheme, url.Host, url.Port == 80 ? "" : ":" + url.Port); 
            viewModel.VideoFolderPath = domainName + _storageProvider.GetPublicUrl(_storageProvider.Combine(_videoRootFolder, part.VideoId.ToString()));

            if (part.VideoId != Guid.Empty) {
                return Combined(
                    ContentShape("Parts_Video", () => shapeHelper.Parts_Video(Model: viewModel, ContentItem: part.ContentItem)),
                    ContentShape("Parts_Video_Summary", () => shapeHelper.Parts_Video_Summary(Model: viewModel, ContentItem: part.ContentItem))
                    );
            }
            else {
                return null;
            }
        }

        //GET
        protected override DriverResult Editor(VideoPart part, dynamic shapeHelper) {
            VideoEditViewModel viewModel = new VideoEditViewModel();
            viewModel.VideoId = part.VideoId;
            viewModel.ThumbnailWidth = part.ThumbnailWidth;
            viewModel.ThumbnailHeight = part.ThumbnailHeight;
            viewModel.ThumbnailCount = part.ThumbnailCount;
            viewModel.VideoStreams = part.VideoStreams;
            viewModel.PlayerWidth = part.PlayerWidth;
            viewModel.PlayerHeight = part.PlayerHeight;
            viewModel.ThumbnailFormat = part.ThumbnailFormat;

            Uri url = _orchardServices.WorkContext.HttpContext.Request.Url;
            string domainName = String.Format("{0}://{1}{2}", url.Scheme, url.Host, url.Port == 80 ? "" : ":" + url.Port);
            viewModel.VideoFolderPath = domainName + _storageProvider.GetPublicUrl(_storageProvider.Combine(_videoRootFolder, part.VideoId.ToString()));

            return ContentShape("Parts_Video_Edit",
                () => shapeHelper.EditorTemplate(
                    TemplateName: "Parts/Video",
                    Model: viewModel,
                    Prefix: Prefix));
        }

        //POST
        protected override DriverResult Editor(VideoPart part, IUpdateModel updater, dynamic shapeHelper) {
            VideoEditViewModel viewModel = new VideoEditViewModel();
            HttpPostedFileBase postedFile = ((Controller)updater).Request.Files["VideoPath"];
            updater.TryUpdateModel(viewModel, Prefix, null, null);

            part.PlayerWidth = viewModel.PlayerWidth;
            part.PlayerHeight = viewModel.PlayerHeight;

            if (postedFile != null && postedFile.ContentLength > 0) {
                part.VideoId = viewModel.VideoId;
                part.ThumbnailFormat = viewModel.ThumbnailFormat;

                if (!viewModel.ExtractCompressedFile) {
                    part.ThumbnailWidth = viewModel.ThumbnailWidth;
                    part.ThumbnailHeight = viewModel.ThumbnailHeight;
                    part.ThumbnailCount = viewModel.ThumbnailCount;
                    part.VideoStreams = viewModel.VideoStreams;

                    ProcessVideo(part, updater);
                }
                else
                    ProcessCompressedFile(part, updater);
            }

            return Editor(part, shapeHelper);
        }

        private bool ProcessVideo(VideoPart part, IUpdateModel updater) {
            bool result = false;
            Guid videoId = Guid.NewGuid();
            
            HttpPostedFileBase postedFile = ((Controller)updater).Request.Files["VideoPath"];
            string fileExt = Path.GetExtension(postedFile.FileName);

            if (_allowedFileFormats.Contains(fileExt)) {
                string relativePath = _storageProvider.Combine(_videoRootFolder, videoId.ToString());
                _storageProvider.TryCreateFolder(relativePath);
                string mediaPath = HostingEnvironment.IsHosted
                            ? HostingEnvironment.MapPath("~/Media/") ?? ""
                            : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Media");
                string storagePath = Path.Combine(mediaPath, _settings.Name);
                string folderPath = _storageProvider.Combine(storagePath, relativePath);
                string filePath = null;
                try {
                    filePath = Path.Combine(folderPath, "source" + fileExt);
                    _videoService.SaveStreamToFile(filePath, postedFile.InputStream);
                }
                catch (Exception ex) {
                    updater.AddModelError("File", T("{0}", ex.Message));
                }

                if (!String.IsNullOrEmpty(filePath)) {
                    try {
                        if (_videoService.CheckForExpressionEncoder()) {
                            if (_videoService.Encode(filePath, folderPath, part)) {
                                part.VideoId = videoId;
                                result = true;
                            }
                        }
                        else {
                            updater.AddModelError("File", T("Microsoft Expression Encoder not found! If you want to encode video files on the server you must install Expression Encoder Pro."));
                        }
                    }
                    catch (Exception ex) {
                        updater.AddModelError("File", T("{0}", ex.Message));
                    }
                }
            }
            else {
                updater.AddModelError("File", T(String.Format("The provided video file format \"{0}\" isn't supported.", fileExt)));
            }
            
            return result;
        }
        private bool ProcessCompressedFile(VideoPart part, IUpdateModel updater) {
            bool result = false;
            Guid videoId = Guid.NewGuid();
            
            HttpPostedFileBase postedFile = ((Controller)updater).Request.Files["VideoPath"];
            string fileExt = Path.GetExtension(postedFile.FileName);

            if (_allowedCompressedFileFormats.Contains(fileExt)) {
                string relativePath = _storageProvider.Combine(_videoRootFolder, videoId.ToString());
                _storageProvider.TryCreateFolder(relativePath);
                string mediaPath = HostingEnvironment.IsHosted
                            ? HostingEnvironment.MapPath("~/Media/") ?? ""
                            : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Media");
                string storagePath = Path.Combine(mediaPath, _settings.Name);
                string folderPath = _storageProvider.Combine(storagePath, relativePath);
                string filePath = null;
                try {
                    filePath = Path.Combine(folderPath, fileExt);
                    filePath = _videoService.SaveStreamToFile(filePath, postedFile.InputStream);
                }
                catch (Exception ex) {
                    updater.AddModelError("File", T("{0}", ex.Message));
                }
                if (!String.IsNullOrEmpty(filePath)) {
                    try {
                        _videoService.UnzipMediaFileArchive(folderPath, filePath);

                        string[] ismFiles = Directory.GetFiles(folderPath, "*.ism", SearchOption.TopDirectoryOnly);
                        string[] ismcFiles = Directory.GetFiles(folderPath, "*.ismc", SearchOption.TopDirectoryOnly);
                        string[] ismvFiles = Directory.GetFiles(folderPath, "*.ismv", SearchOption.TopDirectoryOnly);
                        string[] thumbnailFiles = Directory.GetFiles(folderPath, String.Format("*.{0}", part.ThumbnailFormat.ToString().ToLower()), SearchOption.TopDirectoryOnly);

                        bool isPassCheck = true;
                        if (ismFiles.Count(f => f.EndsWith(".ism")) != 1) {
                            updater.AddModelError("File", T("The provided compressed file must contain exacty one .ism file"));
                            isPassCheck = false;
                        }
                        if (ismcFiles.Length != 1) {
                            updater.AddModelError("File", T("The provided compressed file must contain exacty one .ismc file"));
                            isPassCheck = false;
                        }
                        if (ismvFiles.Length == 0) {
                            updater.AddModelError("File", T("The provided compressed file must contain at least one .ismv file"));
                            isPassCheck = false;
                        }
                        if (thumbnailFiles.Length == 0) {
                           updater.AddModelError("File", T(String.Format("The provided compressed file must contain at least one .{0} file", part.ThumbnailFormat.ToString().ToLower())));
                           isPassCheck = false;
                        }

                        if (!isPassCheck) {
                            Directory.Delete(folderPath, true);
                        }
                        else {
                            //Rename the .ism file to ss.ism
                            File.Move(Path.Combine(folderPath, ismFiles[0]), Path.Combine(folderPath, "ss.ism"));

                            //Rename thumbnails files
                            thumbnailFiles = thumbnailFiles.OrderBy(t => t).ToArray();
                            for (int i = 0; i < thumbnailFiles.Length; i++)
			                {
                    	        File.Move(Path.Combine(folderPath, thumbnailFiles[i]), Path.Combine(folderPath, String.Format("ss-{0}.{1}", i + 1, part.ThumbnailFormat.ToString().ToLower())));
			                }

                            //Get video duration
                            long duration;
                            string ismc = File.ReadAllText(Path.Combine(folderPath, ismcFiles[0]));
                            Match match = Regex.Match(ismc, "Duration=\"(\\d+)\"");
                            if (match.Groups.Count > 1 && long.TryParse(match.Groups[1].Value, out duration)) {
                                part.Duration = duration;
                            }
                            
                            part.ThumbnailCount = thumbnailFiles.Length;
                            part.VideoId = videoId;
                        }

                        result = true;
                    }
                    catch (Exception ex) {
                        updater.AddModelError("File", T("{0}", ex.Message));
                    }
                }
            }
            else {
                updater.AddModelError("File", T(String.Format("The provided compressed file format \"{0}\" isn't supported.", fileExt)));
            }

            return result;
        }

    }
}